home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / Other Langs / Tickle-4.0 (tcl) / tcl / extend / tclsrc / help.tcl < prev    next >
Encoding:
Text File  |  1993-10-26  |  9.8 KB  |  326 lines  |  [TEXT/MPS ]

  1. #
  2. # help.tcl --
  3. #
  4. # Tcl help command. (see TclX manual)
  5. #------------------------------------------------------------------------------
  6. # Copyright 1992-1993 Karl Lehenbauer and Mark Diekhans.
  7. #
  8. # Permission to use, copy, modify, and distribute this software and its
  9. # documentation for any purpose and without fee is hereby granted, provided
  10. # that the above copyright notice appear in all copies.  Karl Lehenbauer and
  11. # Mark Diekhans make no representations about the suitability of this
  12. # software for any purpose.  It is provided "as is" without express or
  13. # implied warranty.
  14. #------------------------------------------------------------------------------
  15. # The help facility is based on a hierarchical tree of subjects (directories)
  16. # and help pages (files).  There is a virtual root to this tree. The root
  17. # being the merger of all "help" directories found along the $auto_path
  18. # variable.
  19. #------------------------------------------------------------------------------
  20. # $Id: help.tcl,v 2.5 1993/06/24 07:30:29 markd Exp $
  21. #------------------------------------------------------------------------------
  22. #
  23.  
  24. #@package: TclX-help help helpcd helppwd apropos
  25.  
  26. #------------------------------------------------------------------------------
  27. # Return a list of help root directories.
  28.  
  29. proc help:RootDirs {} {
  30.     global auto_path
  31.     set roots {}
  32.     foreach dir $auto_path {
  33.         if [file isdirectory $dir/help] {
  34.             lappend roots $dir/help
  35.         }
  36.     }
  37.     return $roots
  38. }
  39.  
  40. #------------------------------------------------------------------------------
  41. # Take a path name which might have "." and ".." elements and flatten them out.
  42. # Also removes trailing and adjacent "/", unless its the only character.
  43.  
  44. proc help:FlattenPath pathName {
  45.     set newPath {}
  46.     foreach element [split $pathName /] {
  47.         if {"$element" == "." || [lempty $element]} continue
  48.  
  49.         if {"$element" == ".."} {
  50.             if {[llength [join $newPath /]] == 0} {
  51.                 error "Help: name goes above subject directory root"}
  52.             lvarpop newPath [expr [llength $newPath]-1]
  53.             continue
  54.         }
  55.         lappend newPath $element
  56.     }
  57.     set newPath [join $newPath /]
  58.  
  59.     # Take care of the case where we started with something line "/" or "/."
  60.  
  61.     if {("$newPath" == "") && [string match "/*" $pathName]} {
  62.         set newPath "/"
  63.     }
  64.         
  65.     return $newPath
  66. }
  67.  
  68. #------------------------------------------------------------------------------
  69. # Given a pathName relative to the virtual help root, convert it to a list of
  70. # real file paths.  A list is returned because the path could be "/", returning
  71. # a list of all roots. The list is returned in the same order of the auto_path
  72. # variable. If path does not start with a "/", it is take as relative to the
  73. # current help subject.  Note:  The root directory part of the name is not
  74. # flattened.  This lets other commands pick out the part relative to the
  75. # one of the root directories.
  76.  
  77. proc help:ConvertPath pathName {
  78.     global TCLENV
  79.  
  80.     if {![string match "/*" $pathName]} {
  81.         if {"$TCLENV(help:curSubject)" == "/"} {
  82.             set pathName "/$pathName"
  83.         } else {
  84.             set pathName "$TCLENV(help:curSubject)/$pathName"
  85.         }
  86.     }
  87.     set pathName [help:FlattenPath $pathName]
  88.  
  89.     # If the virtual root is specified, return a list of directories.
  90.  
  91.     if {$pathName == "/"} {
  92.         return [help:RootDirs]
  93.     }
  94.  
  95.     # Not the virtual root find the first match.
  96.  
  97.     foreach dir [help:RootDirs] {
  98.         if [file readable $dir/$pathName] {
  99.             return [list $dir/$pathName]
  100.         }
  101.     }
  102.     error "\"$pathName\" does not exist"
  103. }
  104.  
  105. #------------------------------------------------------------------------------
  106. # Return the virtual root relative name of the file given its absolute path.
  107. # The root part of the path should not have been flattened, as we would not
  108. # be able to match it.
  109.  
  110. proc help:RelativePath pathName {
  111.     foreach dir [help:RootDirs] {
  112.         if {[csubstr $pathName 0 [clength $dir]] == $dir} {
  113.             set name [csubstr $pathName [clength $dir] end]
  114.             if {$name == ""} {set name /}
  115.             return $name
  116.         }
  117.     }
  118.     if ![info exists found] {
  119.         error "problem translating \"$pathName\""
  120.     }
  121.  
  122. }
  123.  
  124. #------------------------------------------------------------------------------
  125. # Given a list of path names to subjects generated by ConvertPath, return
  126. # the contents of the subjects.  Two lists are returned, subjects under that
  127. # subject and a list of pages under the subject.  Both lists are returned
  128. # sorted.  This merges all the roots into a virtual root.  pathName is the
  129. # string that was passed to ConvertPath and is used for error reporting.
  130. # *.brk files are not returned.
  131.  
  132. proc help:ListSubject {pathName pathList subjectsVar pagesVar} {
  133.     upvar $subjectsVar subjects $pagesVar pages
  134.  
  135.     set subjects {}
  136.     set pages {}
  137.     set foundDir 0
  138.     foreach dir $pathList {
  139.         if ![file isdirectory $dir] continue
  140.         set foundDir 1
  141.         foreach file [glob -nocomplain $dir/*] {
  142.             if [string match *.brf $file] continue
  143.             if [file isdirectory $file] {
  144.                 lappend subjects [file tail $file]/
  145.             } else {
  146.                 lappend pages [file tail $file]
  147.             }
  148.         }
  149.     }
  150.     if !$foundDir {
  151.         error "\"$pathName\" is not a subject"
  152.     }
  153.     set subjects [lsort $subjects]
  154.     set pages [lsort $pages]
  155.     return {}
  156. }
  157.  
  158. #------------------------------------------------------------------------------
  159. # Display a line of output, pausing waiting for input before displaying if the
  160. # screen size has been reached.  Return 1 if output is to continue, return
  161. # 0 if no more should be outputed, indicated by input other than return.
  162. #
  163.  
  164. proc help:Display line {
  165.     global TCLENV
  166.     if {$TCLENV(help:lineCnt) >= 23} {
  167.         set TCLENV(help:lineCnt) 0
  168.         puts stdout ":" nonewline
  169.         flush stdout
  170.         gets stdin response
  171.         if {![lempty $response]} {
  172.             return 0}
  173.     }
  174.     puts stdout $line
  175.     incr TCLENV(help:lineCnt)
  176. }
  177.  
  178. #------------------------------------------------------------------------------
  179. # Display a help page (file).
  180.  
  181. proc help:DisplayPage filePath {
  182.  
  183.     set inFH [open $filePath r]
  184.     while {[gets $inFH fileBuf] >= 0} {
  185.         if {![help:Display $fileBuf]} {
  186.             break}
  187.     }
  188.     close $inFH
  189. }    
  190.  
  191. #------------------------------------------------------------------------------
  192. # Display a list of file names in a column format. This use columns of 14 
  193. # characters 3 blanks.
  194.  
  195. proc help:DisplayColumns {nameList} {
  196.     set count 0
  197.     set outLine ""
  198.     foreach name $nameList {
  199.         if {$count == 0} {
  200.             append outLine "   "}
  201.         append outLine $name
  202.         if {[incr count] < 4} {
  203.             set padLen [expr 17-[clength $name]]
  204.             if {$padLen < 3} {
  205.                set padLen 3}
  206.             append outLine [replicate " " $padLen]
  207.         } else {
  208.            if {![help:Display $outLine]} {
  209.                return}
  210.            set outLine ""
  211.            set count 0
  212.         }
  213.     }
  214.     if {$count != 0} {
  215.         help:Display [string trimright $outLine]}
  216.     return
  217. }
  218.  
  219. #------------------------------------------------------------------------------
  220. # Display help on help, the first occurance of a help page called "help" in
  221. # the help root.
  222.  
  223. proc help:HelpOnHelp {} {
  224.     set helpPage [lindex [help:ConvertPath /help] 0]
  225.     if [lempty $helpPage] {
  226.         error "No help page on help found"
  227.     }
  228.     help:DisplayFile $helpPage
  229. }
  230.  
  231. #------------------------------------------------------------------------------
  232. # Help command.
  233.  
  234. proc help {{what {}}} {
  235.     global TCLENV
  236.  
  237.     set TCLENV(help:lineCnt) 0
  238.  
  239.     # Special case "help help", so we can get it at any level.
  240.  
  241.     if {($what == "help") || ($what == "?")} {
  242.         help:HelpOnHelp
  243.         return
  244.     }
  245.  
  246.     set pathList [help:ConvertPath $what]
  247.     if [file isfile [lindex $pathList 0]] {
  248.         help:DisplayPage [lindex $pathList 0]
  249.         return
  250.     }
  251.  
  252.     help:ListSubject $what $pathList subjects pages
  253.     set relativeDir [help:RelativePath [lindex $pathList 0]]
  254.  
  255.     if {[llength $subjects] != 0} {
  256.         help:Display "\nSubjects available in $relativeDir:"
  257.         help:DisplayColumns $subjects
  258.     }
  259.     if {[llength $pages] != 0} {
  260.         help:Display "\nHelp pages available in $relativeDir:"
  261.         help:DisplayColumns $pages
  262.     }
  263. }
  264.  
  265.  
  266. #------------------------------------------------------------------------------
  267. # helpcd command.  The name of the new current directory is assembled from the
  268. # current directory and the argument.
  269.  
  270. proc helpcd {{dir /}} {
  271.     global TCLENV
  272.  
  273.     set pathName [lindex [help:ConvertPath $dir] 0]
  274.  
  275.     if {![file isdirectory $pathName]} {
  276.         error "Helpcd: \"$dir\" is not a subject"}
  277.  
  278.     set TCLENV(help:curSubject) [help:RelativePath $pathName]
  279.     return
  280. }
  281.  
  282. #------------------------------------------------------------------------------
  283. # Helpcd main.
  284.  
  285. proc helppwd {} {
  286.         global TCLENV
  287.         echo "Current help subject: $TCLENV(help:curSubject)"
  288. }
  289.  
  290. #------------------------------------------------------------------------------
  291. # apropos command.  This search the 
  292.  
  293. proc apropos {regexp} {
  294.     global TCLENV
  295.  
  296.     set TCLENV(help:lineCnt) 0
  297.  
  298.     set ch [scancontext create]
  299.     scanmatch -nocase $ch $regexp {
  300.         set path [lindex $matchInfo(line) 0]
  301.         set desc [lrange $matchInfo(line) 1 end]
  302.         if {![help:Display [format "%s - %s" $path $desc]]} {
  303.             set stop 1
  304.             return}
  305.     }
  306.     set stop 0
  307.     foreach dir [help:RootDirs] {
  308.         foreach brief [glob -nocomplain $dir/*.brf] {
  309.             set briefFH [open $brief]
  310.             scanfile $ch $briefFH
  311.             close $briefFH
  312.             if $stop break
  313.         }
  314.         if $stop break
  315.     }
  316.     scancontext delete $ch
  317. }
  318.  
  319. #------------------------------------------------------------------------------
  320. # One time initialization done when the file is sourced.
  321. #
  322. global TCLENV
  323.  
  324. set TCLENV(help:curSubject) "/"
  325.